package netty.rpc.client;

import netty.rpc.common.exceptions.ServiceInstanceNotFoundException;
import netty.rpc.core.NettyRpcClientHolder;
import netty.rpc.core.NettyRpcInterface;
import netty.rpc.core.ServiceRegistry;
import netty.rpc.server.RpcResponse;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

/**
 * 接口动态代理
 * @author Jim
 * @version 1.0.0
 * @descripition:
 * @date 2022/5/14 12:35
 */
public class RpcInterfaceProxy {

    private static final Logger logger = LogManager.getLogger(RpcInterfaceProxy.class);

    public static Object createProxy(Class rpcInterfaceClass){
        return  Proxy.newProxyInstance(RpcInterfaceProxy.class.getClassLoader(),
                new Class<?>[]{rpcInterfaceClass},
                new ServiceProxyInvocationHandler(rpcInterfaceClass));
    }

    static class ServiceProxyInvocationHandler implements InvocationHandler {

        private Class rpcInterfaceClass;

        public ServiceProxyInvocationHandler(Class rpcInterfaceClass) {
            this.rpcInterfaceClass = rpcInterfaceClass;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            NettyRpcInterface nettyRpcInterface = (NettyRpcInterface) rpcInterfaceClass.getAnnotation(NettyRpcInterface.class);
            InterfaceConfig interfaceConfig = createInterfaceConfig(nettyRpcInterface);
            interfaceConfig.setInterfaceClass(rpcInterfaceClass);
            //TODO 校验方法是否暴露给外部服务 如果没有暴露 禁止访问
            NettyRpcClient nettyRpcClient = NettyRpcClientHolder.get(interfaceConfig.getServiceName());
            if (nettyRpcClient == null){
                nettyRpcClient = new NettyRpcClient(interfaceConfig);
                NettyRpcClientHolder.put(interfaceConfig.getServiceName(), nettyRpcClient);
            }

            RpcRequest request = createReqeust(interfaceConfig,method,args);
            RpcResponse response = nettyRpcClient.remoteCall(request);
            return response.getResult();
        }

        private RpcRequest createReqeust(InterfaceConfig interfaceConfig, Method method, Object[] args) {
            RpcRequest request = new RpcRequest();
            request.setRequestId(UUID.randomUUID().toString().replace("-",""));
            request.setMethodName(method.getName());
            request.setParameterTypes(method.getParameterTypes());
            request.setClazz(interfaceConfig.getInterfaceClass());
            request.setArgs(args);
            return request;
        }


        private InterfaceConfig createInterfaceConfig(NettyRpcInterface nettyRpcInterface) {
            ServiceRegistry registry = ServiceRegistry.getInstance();
            String serviceId = String.format("%s:%s", nettyRpcInterface.value(),nettyRpcInterface.version());
            //TODO 负载均衡策略
            //随机选择一个服务实例
            String instanceAddress =  registry.luckyPick(serviceId);
            if (instanceAddress == null){
                logger.error("lucky pick "+serviceId+" failure.");
                throw new ServiceInstanceNotFoundException("lucky pick "+serviceId+" failure.");
            }
            logger.info("serviceId: " + serviceId + " picked instanceAddress "+ instanceAddress);
            InterfaceConfig interfaceConfig = new InterfaceConfig();
            interfaceConfig.setHost(instanceAddress.split(":")[0]);
            interfaceConfig.setPort(Integer.valueOf(instanceAddress.split(":")[1]));
            interfaceConfig.setServiceName(nettyRpcInterface.value());
            interfaceConfig.setTimeOut(nettyRpcInterface.timeOut());
            return interfaceConfig;
        }
    }
}
